package jmind.asteroidea.proxy;

import com.alibaba.fastjson.JSON;
import jmind.asteroidea.annotations.StarfishWrist;
import jmind.asteroidea.core.AsteroideaContext;
import jmind.asteroidea.core.AsteroideaContextHolder;
import jmind.asteroidea.core.AsteroideaUtils;
import jmind.asteroidea.core.StarfishSpec;
import jmind.asteroidea.core.parser.TagParser;
import jmind.asteroidea.core.reducer.Reducer;
import jmind.asteroidea.registry.StarfishRegistry;
import jmind.base.util.DataUtil;
import jmind.base.util.reflect.ClassCacheUtil;
import jmind.base.util.reflect.ClassUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * description:
 *
 * @author weibo.xie
 * @date:2022/7/14 下午4:46
 */
@Slf4j
public abstract class AbstractAsteroideaInterfaceProxy {

    private final ApplicationContext applicationContext;
    // 需要被代理的接口class
    protected final Class asteroideaInterfaceClass;

    public AbstractAsteroideaInterfaceProxy(Class asteroideaInterfaceClass, ApplicationContext applicationContext) {
        this.asteroideaInterfaceClass = asteroideaInterfaceClass;
        this.applicationContext = applicationContext;
    }

    /**
     * 生成代理对象
     *
     * @return
     */
    public abstract Object getProxy();

    public static AbstractAsteroideaInterfaceProxy create(AsteroideaProxyEnum proxyEnum, Class clazz, ApplicationContext applicationContext) {
        return proxyEnum == AsteroideaProxyEnum.CGLIB ?
                new CglibAbstractAsteroideaIntefaceProxy(clazz, applicationContext)
                : new JdkAbstractAsteroideaIntefaceProxy(clazz, applicationContext);

    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 特殊操作不予支持避免出现循环依赖
        if (AsteroideaUtils.isSpecialMethod(method)) {
            throw new UnsupportedOperationException("proxy object DO NOT support method:" + method.getName());
        }
        // 代理toString方法，返回有意义的返回值
        if (AsteroideaUtils.isToStringMethod(method)) {
            return getClass() + "##Generated Proxy of " + asteroideaInterfaceClass.getName();
        }

        AsteroideaContext context = AsteroideaContextHolder.getContext();
        if (context == null) {
            StarfishWrist starfishWrist = method.getAnnotation(StarfishWrist.class);
            if (starfishWrist == null) {
                throw new UnsupportedOperationException("must Annotation @StarfishWrist, DO NOT support method:" + method.getName());
            }
            TagParser tagParser = ClassCacheUtil.getInstance().newInstance(starfishWrist.tagParser());
            String[] tags = tagParser.parse(starfishWrist.tags(), AsteroideaUtils.createParamArgs(args), args);
            context = new AsteroideaContext();
            context.setTags(tags);
            context.setReducerClass(starfishWrist.reducer());

        }

        // 如果只有一个tag，直接运行返回
        if (context.isSingle()) {
            StarfishSpec starfishSpec = StarfishRegistry.getInstance().getStarfishSpec(asteroideaInterfaceClass, context.getTags()[0]);
            if (starfishSpec == null) {
                throw new UnsupportedOperationException(String.format("[%s.%s] NO Instance by tag:%s", asteroideaInterfaceClass.getSimpleName(), method.getName(), context.getTags()[0]));
            }
            Object bean = getBean(starfishSpec);
            if (bean == null) {
                throw new UnsupportedOperationException(String.format("[%s.%s] not found Instance by starfishSpec:%s", asteroideaInterfaceClass.getSimpleName(), method.getName(), JSON.toJSONString(starfishSpec)));
            }
            return invoke(method, bean, args);
        }
        Reducer reducer = ClassUtil.instantiate(context.getReducerClass());
        List<StarfishSpec> starfishSpecs = StarfishRegistry.getInstance().getStarfishSpec(asteroideaInterfaceClass, context.getTags());
        if (DataUtil.isEmpty(starfishSpecs)) {
            throw new UnsupportedOperationException(String.format("[%s.%s] not found Instance by tag:%s", asteroideaInterfaceClass.getSimpleName(), method.getName(), Arrays.toString(context.getTags())));
        }
        for (StarfishSpec starfishSpec : starfishSpecs) {
            Object bean = getBean(starfishSpec);
            Object result = invoke(method, bean, args);
            reducer.addResult(starfishSpec.getTag(), result);
            if (reducer.isBreak(starfishSpec.getTag(), result)) {
                break;
            }
        }
        return reducer.reduce();
    }

    /**
     * InvocationTargetException异常由Method.invoke(obj, args...)方法抛出。当被调用的方法的内部抛出了异常而没有被捕获时，将由此异常接收。
     * 所以需要转化一下，抛出原始的异常
     * @param method
     * @param bean
     * @param args
     * @return
     * @throws Throwable
     */
    private Object invoke(Method method, Object bean, Object... args) throws Throwable {
        try {
            return method.invoke(bean, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

    private final Object getBean(StarfishSpec starfishSpec) {
        try {
            return applicationContext.getBean(starfishSpec.getBeanName(), asteroideaInterfaceClass);
        } catch (BeansException e) {
            log.info("try find by {}|{}", starfishSpec.getImplClass(), e.getMessage());
            try {
                // bean 的名字没找到，通过实现类class 找，之所以不直接通过class找，主要是考虑继承关系。
                return applicationContext.getBean(starfishSpec.getImplClass());
            } catch (BeansException e1) {
                log.info("try newInstance {}|{}", starfishSpec.getImplClass(), e1.getMessage());
                // 容器里没有，直接new 一个
                return ClassCacheUtil.getInstance().newInstance(starfishSpec.getImplClass());
            }
        }
    }
}
